//
//  SoundEffectsDisplay.m
//  Display Plugins
//
//  Created by Matthew Walton on 09/09/2004.
//  Copyright 2004-2005 The Growl Project. All rights reserved.
//

#import "SoundEffectsDisplay.h"
#import "SoundEffectsWindowController.h"
#import "SoundEffectsPrefsController.h"
#import "SoundEffectsDefines.h"
#import "GrowlDefines.h"

static unsigned smokeDepth = 0U;

@implementation SoundEffectsDisplay

- (id) init 
{
	if ((self = [super init])) 
	{
		[[NSNotificationCenter defaultCenter] addObserver:self
												 selector:@selector(_smokeGone:)
													 name:@"SmokeGone"
												   object:nil];
	}
	
	return self;
}

- (void) dealloc 
{
	[preferencePane release];
	[clickHandlerEnabled release];
	
	[super dealloc];
}

- (NSPreferencePane *) preferencePane 
{
	if (!preferencePane) 
		preferencePane = [[SoundEffectsPrefsController alloc] initWithBundle:[NSBundle bundleForClass:[SoundEffectsPrefsController class]]];

	return preferencePane;
}

- (BOOL) notification:(NSDictionary *)noteDict matchesRule:(NSDictionary *)rule
{
	NSString * fieldName = [rule objectForKey:@"field"];

	if ([fieldName isEqualToString:@"Title"])
		fieldName = GROWL_NOTIFICATION_TITLE;
	else if ([fieldName isEqualToString:@"Text"])
		fieldName = GROWL_NOTIFICATION_DESCRIPTION;
	else if ([fieldName isEqualToString:@"Notification Name"])
		fieldName = GROWL_NOTIFICATION_NAME;

	if (fieldName != nil)
	{
		NSString * object = [noteDict objectForKey:fieldName];

		if (object == nil)
			return NO;
			
		NSString * value = [rule objectForKey:@"text"];
		NSString * operation = [rule objectForKey:@"operation"];

		if ([operation isEqualToString:@"contains"])
		{
			if ([object rangeOfString:value options:NSCaseInsensitiveSearch].location != NSNotFound)
				return YES;
		}
		else if ([operation isEqualToString:@"does not contains"])
		{
			if ([object rangeOfString:value options:NSCaseInsensitiveSearch].location == NSNotFound)
				return YES;
		}
		else if ([operation isEqualToString:@"equals"])
		{
			if ([[object lowercaseString] isEqualToString:[value lowercaseString]])
				return YES;
		}
		else if ([operation isEqualToString:@"does not equal"])
		{
			if (![[object lowercaseString] isEqualToString:[value lowercaseString]])
				return YES;
		}
	}
	
	return NO;
}

- (void) playSound:(NSString *) name
{
	NSSound * sound = [NSSound soundNamed:name];
	[sound play];
}


- (void) displaySmokeNotificationWithInfo:(NSDictionary *)noteDict
{
	clickHandlerEnabled = [[noteDict objectForKey:@"ClickHandlerEnabled"] retain];
	
	SoundEffectsWindowController *controller = [[SoundEffectsWindowController alloc]
		initWithTitle:[noteDict objectForKey:GROWL_NOTIFICATION_TITLE]
				 text:[noteDict objectForKey:GROWL_NOTIFICATION_DESCRIPTION]
				 icon:[noteDict objectForKey:GROWL_NOTIFICATION_ICON]
			 priority:[[noteDict objectForKey:GROWL_NOTIFICATION_PRIORITY] intValue]
			   sticky:[[noteDict objectForKey:GROWL_NOTIFICATION_STICKY] boolValue]
				depth:smokeDepth
		   identifier:[noteDict objectForKey:GROWL_NOTIFICATION_IDENTIFIER]];

	[controller setTarget:self];
	[controller setAction:@selector(_smokeClicked:)];
	[controller setAppName:[noteDict objectForKey:GROWL_APP_NAME]];
	[controller setAppPid:[noteDict objectForKey:GROWL_APP_PID]];
	[controller setClickContext:[noteDict objectForKey:GROWL_NOTIFICATION_CLICK_CONTEXT]];
	[controller setScreenshotModeEnabled:[[noteDict objectForKey:GROWL_SCREENSHOT_MODE] boolValue]];

	// update the depth for the next notification with the depth given by this new one
	// which will take into account the new notification's height
	smokeDepth = [controller depth] + SoundEffectsPadding;
	[controller startFadeIn];
	[controller release];
}

- (void) displayNotificationWithInfo:(NSDictionary *)noteDict
{
	BOOL played = false;

	NSArray * prefRules = nil;

	READ_GROWL_PREF_VALUE (SoundEffectsRulesPref, SoundEffectsPrefDomain, NSArray *, &prefRules);
	
	if (prefRules != nil)
	{
		unsigned i = 0;
		for (i = 0; i < [prefRules count]; i++)
		{
			NSDictionary * rule = [prefRules objectAtIndex:i];

			if (!played && [self notification:noteDict matchesRule:rule])
			{
				NSNumber * visuals = [rule objectForKey:@"visuals"];
				
				if (visuals == nil || ![visuals boolValue])
					[self displaySmokeNotificationWithInfo:noteDict];
					
				[self playSound:[rule objectForKey:@"soundName"]];
				played = true;
			}
		}
	}

	if (!played)
	{
		[self displaySmokeNotificationWithInfo:noteDict];

		NSString * value = nil;
		READ_GROWL_PREF_VALUE (SoundEffectsSoundNamePref, SoundEffectsPrefDomain, NSString *, &value);
		[self playSound:value];
	}
	
	// For use with "Do Not Disturb"
	
	NSMutableDictionary * dndDict = [NSMutableDictionary dictionaryWithDictionary:noteDict];
	[dndDict  removeObjectForKey:GROWL_NOTIFICATION_ICON];
	[dndDict  removeObjectForKey:GROWL_NOTIFICATION_APP_ICON];

	[[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"DNDGrowlNotification"
		object:nil userInfo:dndDict deliverImmediately:YES];
}

- (void) _smokeGone:(NSNotification *)note 
{
	unsigned notifiedDepth = [[[note userInfo] objectForKey:@"Depth"] intValue];
	//NSLog(@"Received notification of departure with depth %u, my depth is %u\n", notifiedDepth, smokeDepth);
	if (smokeDepth > notifiedDepth) {
		smokeDepth = notifiedDepth;
	}
	//NSLog(@"My depth is now %u\n", smokeDepth);
}

- (void) _smokeClicked:(SoundEffectsWindowController *)controller 
{
	id clickContext;

	if ((clickContext = [controller clickContext])) {
		NSDictionary *userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:
			clickHandlerEnabled, @"ClickHandlerEnabled",
			clickContext,        GROWL_KEY_CLICKED_CONTEXT,
			[controller appPid], GROWL_APP_PID,
			nil];
		[[NSNotificationCenter defaultCenter] postNotificationName:GROWL_NOTIFICATION_CLICKED
															object:[controller appName]
														  userInfo:userInfo];
		[userInfo release];

		//Avoid duplicate click messages by immediately clearing the clickContext
		[controller setClickContext:nil];
	}
}

@end
